﻿//========================================================= 
/**@file NamedPipeDevice.h
 * @brief Windows下命名管道设备
 * 
 * @date 2018-09-10   14:05:15
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_NAMEDPIPEDEVICE_H_
#define _LIBZHOUYB_NAMEDPIPEDEVICE_H_
//--------------------------------------------------------- 
#include "FileDevice.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace base_device {
namespace env_win32 {
//--------------------------------------------------------- 
class NamedPipeHandlerFactory : 
    public IFactory<WinHandler>,
    public IFactory<WinAsyncHandler>,
    public LoggerBehavior
{
public:
    //----------------------------------------------------- 
    /// 将管道名转换为Open.sArg参数
    static string ToArg(const char* name, 
        uint timeoutMs = DEV_WAIT_TIMEOUT, 
        size_t maxInstance = 1)
    {
        string sArg = ArgConvert::ToConfig(NameKey, _strput(name));
        sArg += ArgConvert::ToConfig(TimeoutKey, timeoutMs);
        sArg += ArgConvert::ToConfig(MaxInstanceKey, maxInstance);
        return sArg;
    }
public:
    //----------------------------------------------------- 
    /// 管道名称配置项主键 Name
    static const char NameKey[8];
    /// 管道最大实例数配置项主键 MaxInstance
    static const char MaxInstanceKey[16];
    /// 管道默认等待超时配置项主键 Timeout
    static const char TimeoutKey[8];
    //----------------------------------------------------- 
    virtual bool Create(WinHandler& obj, const char* sArg = NULL)
    {
        size_t len = _strlen(sArg);
        if(len < 1)
        {
            LOGGER(_log.WriteLine("参数为空"));
            return false;
        }
        ArgParser parser;
        StringArguments arg;
        string name = "";
        uint timeoutMs = DEV_WAIT_TIMEOUT;
        size_t maxInstance = 1;
        LOGGER(_log.WriteLine("ParseArg..."));
        if(parser.Parse(sArg, arg) > 0)
        {
            ArgConvert::FromConfig<string>(arg, NameKey, name);
            ArgConvert::FromConfig<uint>(arg, TimeoutKey, timeoutMs);
            ArgConvert::FromConfig<size_t>(arg, MaxInstanceKey, maxInstance);
        }
        else
        {
            name.assign(sArg, len);
        }
        CharConverter cvt;
        LOGGER(_log.WriteLine("CreateNamedPipe..."));
        HANDLE hPipe = CreateNamedPipe(cvt.to_char_t(name.c_str(), name.length()),
            PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, maxInstance, 0, 0, timeoutMs, NULL);
        if(!WinHandler::IsValidHandle(hPipe))
        {
            LOGGER(_log.WriteLine("创建命名管道句柄失败"));
            return false;
        }
        obj.Handle = hPipe;
        LOGGER(_log << "Handler:<" << _hex_num(hPipe) << ">\n");
        return true;
    }
    virtual bool IsValid(const WinHandler& obj)
    {
        return WinHandler::IsValidHandle(obj.Handle);
    }
    virtual void Dispose(WinHandler& obj)
    {
        WinHandler::Close(obj.Handle);
    }
    //----------------------------------------------------- 
    virtual bool Create(WinAsyncHandler& obj, const char* sArg = NULL)
    {
        LOGGER(_log.WriteLine("CreateEvent..."));
        HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if(!WinHandler::IsValidHandle(hEvent))
        {
            WinHandler::Close(obj.Handle);
            LOGGER(_log.WriteLine("创建事件句柄失败"));
            return false;
        }
        LOGGER(_log << "Event Handler:<" << _hex_num(hEvent) << ">\n");
        WinHandler& handle = obj;
        if(!Create(handle, sArg))
        {
            WinHandler::Close(hEvent);
            return false;
        }
        obj.WaitOverlapped.hEvent = hEvent;
        return true;
    }
    virtual bool IsValid(const WinAsyncHandler& obj)
    {
        return WinHandler::IsValidHandle(obj.Handle) &&
            WinHandler::IsValidHandle(obj.WaitOverlapped.hEvent);
    }
    virtual void Dispose(WinAsyncHandler& obj)
    {
        CancelIo(obj.Handle);
        WinHandler::Close(obj.Handle);
        WinHandler::Close(obj.WaitOverlapped.hEvent);
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
/// 命名管道服务端设备
class NamedPipeDevice : public HandlerDevice<
    WinHandlerBaseDevice<WinAsyncHandler, NamedPipeHandlerFactory>, 
    AsyncFileHandlerReader, AsyncFileHandlerWriter>
{
protected:
    //----------------------------------------------------- 
    /// 是否有连接过客户端
    bool _isConnected;
    /// 等待连接
    bool _WaitConnect(OVERLAPPED& overlapped, uint timeoutMs, uint intervalMs, Ref<IInterrupter> interrupter)
    {
        ASSERT_FuncErrInfoRet(Tobool(ResetEvent(overlapped.hEvent)), 
            DeviceError::OperatorStatusErr, "ResetEvent");
        bool bOK = Tobool(ConnectNamedPipe(_hDev.Handle, &overlapped));
        if(!bOK && GetLastError() != ERROR_IO_PENDING)
        {
            _logErr(DeviceError::DevConnectErr);
            return false;
        }
        Timer timer;
        while(timer.Elapsed() < timeoutMs)
        {
            if(!IsOpen())
            {
                _logErr(DeviceError::DevAlreadyCloseErr);
                break;
            }
            DWORD dwRet = WaitForSingleObject(overlapped.hEvent, intervalMs);
            switch(dwRet)
            {
            case WAIT_OBJECT_0:
                _isConnected = true;
                return true;
            case WAIT_TIMEOUT:
                break;
            case WAIT_FAILED:
            default:
                CancelIo(_hDev.Handle);
                break;
            }
            /* 中断支持 */
            if(!interrupter.IsNull() && InterruptBehavior::Implement(*interrupter))
            {
                _logErr(DeviceError::OperatorInterruptErr);
                break;
            }
        }
        return false;
    }
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    /// 返回管道名称
    static string Name(const string& server, const string& name)
    {
        /* \\.\管道\管道名 */
        string pipeName = "\\\\";
        pipeName += server;
        pipeName += "\\Pipe\\";
        pipeName += name;
        return pipeName;
    }
    /**
     * @brief 等待并返回一个可读写的客户端管道设备
     * @date 2018-09-12 10:51
     * 
     * @param [out] dev 成功连接后用户客户端通信的设备
     * @param [in] name 连接的管道名称
     * @warning 需要使用 Name 接口进行格式适配
     *
     * @param [in] timeoutMs [default:DEV_WAIT_TIMEOUT] 需要等待的超时时间
     * @param [in] intervalMs [default:DEV_OPERATOR_INTERVAL] 等待的轮询间隔
     * @param [in] interrupter [default:null] 中断器
     */
    template<class TFileDevice>
    static DeviceError::ErrorCode Wait(TFileDevice& dev, const char* name, 
        uint timeoutMs = DEV_WAIT_TIMEOUT,
        uint intervalMs = DEV_OPERATOR_INTERVAL,
        Ref<IInterrupter> interrupter = Ref<IInterrupter>())
    {
        CharConverter cvt;
        Timer timer;
        bool bOK = false;
        const char_t* pName = cvt.to_char_t(name);
        while(timer.Elapsed() < timeoutMs)
        {
            if(Tobool(WaitNamedPipe(pName, intervalMs)))
            {
                bOK = true;
                break;
            }
            /* 中断支持 */
            if(!interrupter.IsNull() && InterruptBehavior::Implement(*interrupter))
                return DeviceError::OperatorInterruptErr;
        }
        if(!bOK) return DeviceError::WaitTimeOutErr;
        return dev.Open(name) ? DeviceError::Success : DeviceError::DevOpenErr;
    }
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    NamedPipeDevice() : HandlerDevice() {}
    /// 打开服务端管道设备
    bool Open(const char* server, const char* name, uint timeoutMs)
    {
        LOG_FUNC_NAME();
        string s = _strput(server);
        string n = _strput(name);
        LOGGER(_log << "Name:<" << n << ">\n";
        _log << "Server:<" << s << ">\n";
        _log << "TimeoutMs:<" << timeoutMs << ">\n");
        string pipeName = Name(s, n);
        string arg = NamedPipeHandlerFactory::ToArg(pipeName.c_str(), timeoutMs);
        return _logRetValue(HandlerDevice::Open(arg.c_str()));
    }
    /**
     * @brief 等待客户端连接
     * @date 2018-09-12 13:03
     * 
     * @param [in] timeoutMs 等待的超时时间
     * @param [in] intervalMs 轮询的时间间隔
     * @param [in] interrupter 外部需要控制中断的中断器
     */
    bool WaitConnect(uint timeoutMs, uint intervalMs, Ref<IInterrupter> interrupter)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "超时时间:<" << timeoutMs << "ms>,轮询间隔:<" << intervalMs << ">\n");
        ASSERT_DeviceValid(IsOpen());
        OVERLAPPED overlapped;
        SetObjZero(overlapped);
        HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if(!WinHandler::IsValidHandle(hEvent))
        {
            _logErr(DeviceError::DevInitErr, "初始化事件句柄失败");
            return _logRetValue(false);
        }
        overlapped.hEvent = hEvent;
        bool bOK = _WaitConnect(overlapped, timeoutMs, intervalMs, interrupter);
        WinHandler::Close(hEvent);
        return _logRetValue(bOK);
    }
    /**
     * @brief 等待客户端连接,使用自身的相关变量进行触发
     * @date 2018-09-12 13:07
     */
    bool WaitConnect()
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());
        bool bOK = _WaitConnect(_hDev.WaitOverlapped, _waitTimeout, _waitInterval, Interrupter);
        return _logRetValue(bOK);
    }
    /// 返回是否已有客户端连接
    inline bool IsConnected() const
    {
        return _isConnected;
    }
    /**
     * @brief 断开客户端连接
     * @date 2018-09-12 10:53
     */
    bool Disconnect()
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());
        bool bOK = true;
        if(_isConnected)
        {
            bOK = Tobool(DisconnectNamedPipe(_hDev.Handle));
            _isConnected = !bOK;
        }
        return _logRetValue(bOK);
    }
    /// 关闭设备
    virtual void Close()
    {
        Disconnect();
        HandlerDevice::Close();
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
} // namespace env_win32
} // namespace base_device
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_NAMEDPIPEDEVICE_H_
//========================================================= 